// 版权归 瞄你个汪 所有。使用本代码应遵守相关法律法规和Apache 2.0开源许可要求。
package top.geeke.aop;

import cn.hutool.json.JSONUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.util.ContentCachingRequestWrapper;
import top.geeke.toolkit.RequestUtils;
import top.geeke.toolkit.eunm.Description;
import top.geeke.toolkit.http.Result;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;

/**
 * <p>
 * 日志切面
 * </p>
 * @author 瞄你个汪
 * @since 2024/6/6 19:33
 */
@Slf4j
@Aspect
@Component
public class LoggingAspect {
    @Pointcut("execution(* top.geeke.controller.*.*(..))")
    public void controllerMethods() {}

    @Pointcut("execution(* top.geeke.service.*.*(..))")
    public void serviceMethods() {}

    @Around("controllerMethods()")
    public Object invoke(ProceedingJoinPoint point) throws Throwable {
        HttpServletRequest request = RequestUtils.getRequest();
        Assert.notNull(request, "请求不能为null");

        StringBuilder builder = new StringBuilder();
        builder.append("\n========================================================================================================================================");
        builder.append("\n== 请求地址: ").append(RequestUtils.getRequestUrl(request));
        builder.append("\n== 请求方式: ").append(request.getMethod());
        builder.append("\n== 请求参数: ").append(getRequestParameter(request));
        builder.append("\n== 协议版本: ").append(request.getProtocol());
        builder.append("\n== 调用方法: ").append(point.getSignature().getName());
        builder.append("\n== 方法描述: ").append(getControllerDescription(point));
        builder.append("\n== 主机地址: ").append(RequestUtils.getDomain());
        builder.append("\n== 主机 名: ").append(request.getServerName());
        builder.append("\n== 主机 IP: ").append(RequestUtils.getClientIP(request));
        builder.append("\n== IP 归属: ").append(RequestUtils.getClientAddress(null));
        builder.append("\n== 设备平台: ").append(RequestUtils.getDeviceInfo(request));
        try {
            Object result = point.proceed();
            if (result instanceof Result) builder.append("\n== 响应结果：").append(JSONUtil.toJsonStr(result));
            return result;
        } catch (Throwable e) {
            builder.append("\n== 异常信息：").append(e.getMessage());
            throw e;
        } finally {
            builder.append("\n========================================================================================================================================");
            log.info(builder.toString());
        }
    }

    private String getRequestParameter(HttpServletRequest request) {
        String json = null;
        StringBuilder builder = new StringBuilder();
        if (!CachingContentFilter.FORM_CONTENT_TYPE.equalsIgnoreCase(request.getContentType())) {
            ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) request;
            json = IOUtils.toString(wrapper.getContentAsByteArray(), StandardCharsets.UTF_8.name());
        } else {
            builder.append(request.getRemoteUser());
        }
        if (json == null && builder.length() == 0) return null;
        if (builder.length() > 0) builder.delete(builder.length() - 1, builder.length());

        if (builder.length() == 0) return json;
        if (json == null) return builder.toString();

        return JSONUtil.toJsonStr(new String[]{ json, builder.toString() });
    }

    /**
     * 获取控制层的方法描述
     */
    private String getControllerDescription(JoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();

        StringBuilder builder = new StringBuilder();

        Class<?> clazz = method.getDeclaringClass();
        Api api = clazz.getAnnotation(Api.class);
        if (api != null ) {
            builder.append(String.join("", api.tags())).append(" -> ");
        }

        ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
        if (apiOperation == null) {
            Description description = method.getAnnotation(Description.class);
            if (description != null) {
                builder.append(description.value());
            }
        } else {
            builder.append(apiOperation.notes());
        }

        return builder.toString();
    }

    /**
     * 获取服务层的方法描述
     */
    private Object getServiceInfo(JoinPoint joinPoint) {
        return null;
    }
}
